/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/

/**
 * @brief  Semantic actions for translating expressions.
 *
 * @author  WXQ#XDU
 * @date    2025.01
 */

#include <assert.h>

//
// TODO  Consider type conversion in the future.
//


#include "_emit_inner.h"
#include "emitact_expr.inc"  // helper for translating expressions



// post-semantic-action for expression:
//   just pass attribute in bottom-up as arithmetic-expr, or
//   convert arithmetic-expr to boolean-expr for IF(expr) and WHILE(expr).
//
void emit_exit_expression(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;

    // convert arithmetic-expr to boolean-expr for IF, WHILE
    t_tree_ptr   parentNode = tree_get_parent(node);
    if (   tree_is_markedByNT(parentNode, NT_selectionStatement)   // IF(expr)
        || tree_is_markedByNT(parentNode, NT_iterationStatement) ) // WHILE(expr)
    {
        t_tree_ptr      childNode = tree_get_child(node, 0); // assignmentExpr
        t_codeattr_ptr  childAttr = (t_codeattr_ptr)tree_get_annotation(childNode);
        if ( EXPR_IS_ARITH( childAttr ) ) {
            t_codeattr_ptr myAttr = codeattr_new();
            tree_set_annotation(node, myAttr);
            emit_convertExprToBool__(pEmitter->ircodemgrPtr, childAttr, myAttr);
            return;
        }
    }

    // in other situation, just pass attribute from the first child
    emit_passAttributeOfChild__(node, 0);
}


// post-semantic-action for assignmentExpression:
//   emit code for all assignments.
//
void emit_exit_assignmentExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    bool isAssignment = tree_get_childCount(node) > 1;
    if ( false == isAssignment ) {
        // for assignmentExpression -> logicalOrExpression,
        // just pass attribute in bottom-up.
        emit_passAttributeOfChild__(node, 0);
        return;
    }

    // Following translates product:
    // assignmentExpr -> leftValue
    //                   ('='  | '+=' | '-=' | '*=' | '/=' | '%=')
    //                   assignmentExpression

    t_codeattr_ptr myAttr   = codeattr_new();
    myAttr->type = codemgr_getDataType(node);
    tree_set_annotation(node, myAttr);

    t_emitter_ptr  pEmitter       = (t_emitter_ptr)listenerPtr;
    t_tree_ptr     leftValueNode  = tree_get_child(node, 0);
    t_codeattr_ptr leftValueAttr  = tree_get_annotation(leftValueNode);
    t_tree_ptr     rightValueNode = tree_get_child(node, 2);
    t_codeattr_ptr rightValueAttr = (t_codeattr_ptr)tree_get_annotation(rightValueNode);

    EnumTokenKind opKind = tree_get_tokenKind( tree_get_child(node, 1) );
    if (TK_OP_ASSIGN == opKind) {  // for  x = y
        emit_assign__(pEmitter, leftValueAttr, rightValueAttr, myAttr);
        return;
    }

// For  ('+=' | '-=' | '*=' | '/=' | '%=')
    EnumOpCode  opCode;    {
        switch (opKind) {
            case TK_OP_PLUSASSIGN:  opCode = OP_PLUS;   break;
            case TK_OP_MINUSASSIGN: opCode = OP_MINUS;  break;
            case TK_OP_MULASSIGN:   opCode = OP_MUL;    break;
            case TK_OP_DIVASSIGN:   opCode = OP_DIV;    break;
            case TK_OP_MODASSIGN:   opCode = OP_MOD;    break;
            default:          opCode = OP_UNKNOWN__;    break;
        }
    }
    emit_assign_withop__(pEmitter, opCode, leftValueAttr, rightValueAttr, myAttr);

}  // end of emit_exit_assignmentExpr()



// post-semantic-action for logicalOrExpression:
//   emit code for   E ->  E1 ( "||" E2 )*
//
void emit_exit_logicalOrExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_binaryOptr_loop_(listenerPtr, node);
}


// post-semantic-action for logicalAndExpression:
//   emit code for   E ->  E1 ( "&&" E2 )*
//
void emit_exit_logicalAndExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_binaryOptr_loop_(listenerPtr, node);

    // If E is the operand of "||" and E(E1) is an arithmetic expr,
    // then convert it to a boolean expr

    t_codeattr_ptr   myAttr = (t_codeattr_ptr) tree_get_annotation(node);
    t_tree_ptr   parentNode = tree_get_parent(node);
    if ( EXPR_IS_BOOL(myAttr) )
        return;
    if ( tree_get_childCount(parentNode) == 1 )
        return;

    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    emit_convertExprToBool__(pEmitter->ircodemgrPtr, myAttr, myAttr);
}


// post-semantic-action for equalityExpression:
//   emit code for   E ->  E1 ( ( "==" | "!=" ) E2 )*
//
void emit_exit_equalityExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_binaryOptr_loop_(listenerPtr, node);

    // If E is the operand of "&&" and E(E1) is an arithmetic expr,
    // then convert it to a boolean expr

    t_codeattr_ptr   myAttr = (t_codeattr_ptr) tree_get_annotation(node);
    t_tree_ptr   parentNode = tree_get_parent(node);
    if ( EXPR_IS_BOOL(myAttr) )
        return;
    if ( tree_get_childCount(parentNode) == 1 )
        return;

    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    emit_convertExprToBool__(pEmitter->ircodemgrPtr, myAttr, myAttr);
}


// post-semantic-action for relationalExpression:
//   emit code for    E ->  E1 ( ( "<" | "<=" | ">" | ">=" ) E2 )*.
//
void emit_exit_relationalExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_binaryOptr_loop_(listenerPtr, node);
}


// post-semantic-action for additiveExpression:
//   emit code for    E ->  E1 ( ( "+" | "-" ) E2 )*.
//
void emit_exit_additiveExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_binaryOptr_loop_(listenerPtr, node);
}


// post-semantic-action for multiplicativeExpression:
//   emit code for    E ->  E1 ( ( "*" | "/" | "%" ) E2 )*.
//
void emit_exit_multiplicativeExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    emit_binaryOptr_loop_(listenerPtr, node);
}


// post-semantic-action for unaryExpression:
//   emit code for    E -> E | ( "+" | "-" | "!" ) E
//
void emit_exit_unaryExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter     = (t_emitter_ptr)listenerPtr;
    t_tree_ptr     childNode0   = tree_get_child(node, 0);
    EnumTokenKind  opTokenKind  = tree_get_tokenKind(childNode0);

// CASE 1: unaryExpr -> primaryExpr, don't emit new codes
// CASE 2: unaryExpr ->  + E,        don't emit new codes
    switch( opTokenKind ) {
        case TK_OP_NOT:
        case TK_OP_MINUS:
            break;          // flow down to translate -E and !E

        case TK_OP_PLUS:    // for +E, just pass attributes of E
            emit_passAttributeOfChild__(node, 1);
            return ;
        default:            // for others, just pass attributes
            emit_passAttributeOfChild__(node, 0);
            return ;
    }

    // get attribute of the operand , E  in "-E" or "! E"
    t_tree_ptr     operandNode = tree_get_child(node, 1);
    t_codeattr_ptr operandAttr = (t_codeattr_ptr)tree_get_annotation(operandNode);
    t_codeattr_ptr myAttr;

// CASE 3: unaryExpr ->  - E,  emit code:   t1 = - arg1
    if (TK_OP_MINUS == opTokenKind) {
        myAttr = codeattr_new();
        tree_set_annotation( node, myAttr );
        myAttr->type  = codemgr_getDataType(node);
        myAttr->place = codemgr_newTempVar(pEmitter->ircodemgrPtr);
        t_ir_address_ptr operandAddr = emit_getExprAddress__(pEmitter->ircodemgrPtr, operandAttr);
        codemgr_emit(pEmitter->ircodemgrPtr,  OP_MINUS_UNARY,
                     operandAddr, NULL, myAttr->place);
        return ;
    }

// CASE 4: unaryExpr ->  ! E
    // Apply the Short-Circuit evaluating  style
    // according to section 4.8.5 in our textbook

    if ( EXPR_IS_ARITH(operandAttr) ) {
        // If the operand, E, is NOT a boolean-expr, then
        // convert it to boolean
        myAttr = codeattr_new();
        emit_convertExprToBool__( pEmitter->ircodemgrPtr, operandAttr, myAttr );
    }
    else { // EXPR_IS_BOOL( operandAttr ) )
        myAttr = codeattr_copy( operandAttr );
    }

    // interchange  chains for "!" (logic NOT)
    t_list_ptr    tmp  = myAttr->trueChain;
    myAttr->trueChain  = myAttr->falseChain;
    myAttr->falseChain = tmp;
    tree_set_annotation( node, myAttr );
}


// post-semantic-action for primaryExpression:
//   compute the attribute of expr-value
//
void emit_exit_primaryExpr(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr    pEmitter = (t_emitter_ptr) listenerPtr;
    t_tree_ptr     childNode0 = tree_get_child(node, 0);
    EnumTokenKind  childKind0 = tree_get_tokenKind(childNode0);
    t_codeattr_ptr childAttr0 = (t_codeattr_ptr) tree_get_annotation(childNode0);

// CASE  1:  For a literal, only computer its attributes
    if ( TK_INT_LITERAL == childKind0 || TK_REAL_LITERAL == childKind0 ) {
        t_codeattr_ptr myAttr = codeattr_new();
        tree_set_annotation( node, myAttr );
        myAttr->place = codemgr_newToken(pEmitter->ircodemgrPtr, childNode0);
        myAttr->type  = codemgr_getDataType(node);
        return ;
    }

// CASE  2:  For "(E)", just pass the attribute of E
    if ( TK_LPAREN == childKind0 ) {
        emit_passAttributeOfChild__(node, 1);
        return ;
    }

// CASE  3:   For functionCall, just pass the attribute of return-value.
    if ( tree_is_markedByNT(childNode0, NT_functionCall) ) {
        // This attribute has computed by emit_exit_functionCall()
        t_codeattr_ptr myAttr = codeattr_getReference(childAttr0 );
        tree_set_annotation( node, myAttr );
        return ;
    }

// CASE  4:  For  E -> leftValue

// CASE  4.1:  For E ->leftValue -> id , just pass the attribute of id
    if (NULL == childAttr0->offset) {
        // The childAttr0 has computed by emit_exit_leftValue()
        t_codeattr_ptr myAttr = codeattr_getReference(childAttr0 );
        tree_set_annotation( node, myAttr );
        return ;
    }

// CASE 4.2:  For E ->leftValue -> id[e1][e2]...[en],
//           emit:   t1 = V.place[V.offset]
    t_codeattr_ptr myAttr = codeattr_new();
    tree_set_annotation(node, myAttr);
    myAttr->type  =  codemgr_getDataType(node);
    myAttr->place = emit_getExprAddress__(pEmitter->ircodemgrPtr, childAttr0);
    myAttr->offset = NULL;

}  // end of emit_exit_primaryExpr()



// post-semantic-action for leftValue:
//  (1) For a scalar variable, just compute attribute;
//  (2) For an array element, ID[i1]...[in], emit codes that compute
//      the address of the element according to section 4.7.2 of out textbook.
//      We translate  following operations to IR-codes:
//          v1 = i1, v2 = v1 * d2 + i2, ... vn = ...
//          t999 = vn *w
//      and compute attributes for element reference as:
//          .place  =  address_of_array
//          .offset = t999
//
void emit_exit_leftValue(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter  = (t_emitter_ptr)listenerPtr;
    t_tree_ptr     pIdNode   = tree_get_child ( node, 0 ); // var-name
    t_symentry_ptr pSymEntry = tree_get_symbolEntry(pIdNode);
    t_type_ptr     varType   = symentry_type(pSymEntry);  // type of variable

    // For reference to a scalar or the name of array without subscript,
    // don't emit new codes,   just compute attributes
    if ( type_is_scalar(varType) || tree_get_childCount(node) == 1 )
    {
        t_codeattr_ptr myAttr = codeattr_new();
        myAttr->place = codemgr_newToken(pEmitter->ircodemgrPtr, pIdNode);
        myAttr->offset= NULL;
        myAttr->type  = varType;
        tree_set_annotation(node, myAttr);
        return;
    }

//  Following for a reference to array element:  id[E1][E2]...[En]
//  emit codes that computer the address of this element

    t_ircode_ptr     codePtr;
    t_ir_address_ptr EL_place = NULL;  // attribute EL.place in textbook
    t_list_iterator  itr;              // prepare to visit E1, E2, ..., En
    LIST_ITERATOR_INIT ( &itr, tree_get_children(node) );
    t_list_node_ptr subNode = LIST_ITERATOR_BEGIN ( &itr ); // ID node
    t_list_node_ptr end     = LIST_ITERATOR_END   ( &itr );

    subNode = LIST_ITERATOR_NEXT( &itr );  // jump the ID node
    for (int EL_dim=1; subNode != end; subNode = LIST_ITERATOR_NEXT(&itr) )
    {
        subNode = LIST_ITERATOR_NEXT( &itr ); // jump "["
        t_tree_ptr       exprNode = (t_tree_ptr) list_node_get_value(subNode);
        t_codeattr_ptr   exprAttr = (t_codeattr_ptr) tree_get_annotation(exprNode);
        t_ir_address_ptr exprAddr = emit_getExprAddress__(pEmitter->ircodemgrPtr, exprAttr);

        if (1 == EL_dim ) {             // for Dim 1
            EL_place = exprAddr;
        } else {                        // for Dim 2..N
            // translating  v[k] = v[k-1] * d[k] + i[k]
            int dimLen = type_var_dimlength(varType, EL_dim);  // the d[k]
            t_ir_address_ptr  dk_Addr = codemgr_newNumber(pEmitter->ircodemgrPtr, dimLen);
            t_ir_address_ptr tempAddr = codemgr_newTempVar(pEmitter->ircodemgrPtr);

            // Emit: T = EL1.place * dk
            codePtr = codemgr_emit(pEmitter->ircodemgrPtr,
                         OP_MUL, EL_place, dk_Addr, tempAddr);
            codePtr->comment.text = CODEDESC_ARR_VK1;

            // Emit: T = T + E.place
            codePtr = codemgr_emit(pEmitter->ircodemgrPtr,
                         OP_PLUS, tempAddr, exprAddr, tempAddr);
            codePtr->comment.text = CODEDESC_ARR_VK2;

            EL_place = tempAddr;  // EL.place = T, it is v[k]
        }

        ++ EL_dim;
        subNode = LIST_ITERATOR_NEXT( &itr ); // jump "]"
    }

    t_codeattr_ptr myAttr = codeattr_new();
    tree_set_annotation( node, myAttr );
    myAttr->type  = codemgr_getDataType(node);

    // In section 4.7.2 of out textbook, for production V -> EL ],
    // emit code like
    //       V.place = EL.array - C
    // BUT this C is always 0 in AMC.
    // Here, we emit this code for comparing easily to our textbook.
    myAttr->place = codemgr_newTempVar( pEmitter->ircodemgrPtr );
    t_ir_address_ptr
    arrayNameAddr = codemgr_newToken(pEmitter->ircodemgrPtr, pIdNode);
    codePtr = codemgr_emit( pEmitter->ircodemgrPtr,  OP_MINUS,
                            arrayNameAddr,
                            pEmitter->ircodemgrPtr->falseAddress,
                            myAttr->place);
    codePtr->comment.text = CODEDESC_ARR_ADDR;

    // emit  V.offset = EL.place * w , V.offset is a temporary variable
    myAttr->offset = codemgr_newTempVar( pEmitter->ircodemgrPtr );
    size_t        elementWidth = type_width( type_var_base(varType) );
    t_ir_address_ptr widthAddr = codemgr_newNumber(pEmitter->ircodemgrPtr,elementWidth);
    codePtr = codemgr_emit( pEmitter->ircodemgrPtr,  OP_MUL,
                            EL_place, widthAddr,myAttr->offset);
    codePtr->comment.text = CODEDESC_ARR_VKn;

}


// post-semantic-action for functionCall:
// emit code:
//       call func, N        --- if func returns no value
// or
//       t1 = call func, N   --- if func returns a  value
//
void emit_exit_functionCall(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr    pEmitter  = (t_emitter_ptr)listenerPtr;

    // children are :   func_name ( argList )
    //  and argList is one of:
    //   (1) epsilon for empty argument list
    //   (2) one or more arguments with ",", such as: arg1, arg2, ..., argn
    t_tree_ptr       pIdNode   = tree_get_child ( node, 0 ); // func_name
    t_tree_ptr   argListNode   = tree_get_child ( node, 2 );
    t_list_ptr       argList   = tree_get_children( argListNode );
    t_ircode_id      argCount  ; {  // computer number of arguments
        t_tree_ptr arg0 = list_get_first_value(argList);
        if ( tree_is_epsilon(arg0) )   // no arguments
            argCount = 0;
        else {
            argCount = (t_ircode_id) list_size(argList);
            argCount = (argCount + 1) >> 1; // excluding commas
        }
    }

    t_codeattr_ptr myAttr  = codeattr_new();
    tree_set_annotation(node, myAttr);
    myAttr->type  = codemgr_getDataType(node);

    t_ir_address_ptr funcAddr  = codemgr_newToken(pEmitter->ircodemgrPtr, pIdNode);
    t_ir_address_ptr countAddr = codemgr_newNumber(pEmitter->ircodemgrPtr, argCount);
    EnumOpCode       opCode    = OP_CALL;
    if (TK_TYPE_VOID == type_var_basekind(myAttr->type)) {
        myAttr->place = NULL;   // function has no return-value
    } else {
        opCode = OP_CALL_GET;   // function has return-value
        myAttr->place = codemgr_newTempVar(pEmitter->ircodemgrPtr);
    }

    t_ircode_ptr codePtr = codemgr_emit(pEmitter->ircodemgrPtr,  opCode,
                                funcAddr, countAddr, myAttr->place);
    codePtr->comment.text = CODEDESC_CALL;
}


// post-semantic-action for functionCall:
//   emit code:  param  arg.place
//
//  The form of argumentList is as follows(production):
//     argumentList ->  epsilon
//             | assignmentExpr  ( , assignmentExpression )*
//
void emit_exit_argumentList(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    t_tree_ptr   childNode0 = tree_get_child(node, 0);
    if ( tree_is_epsilon(childNode0) )   // no arguments
        return;

    list_foreach(tree_get_children(node), emit_param__, pEmitter);
}


// Semantic-action for terminal.
//
// We only care following situations:
// (1) boolean operator: ||, &&,
//            records the first IR-code of the right operand,
// (2) ELSE : emit "goto -" and make-chain into parent.tc.
//
void emit_visit_terminal(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter  = (t_emitter_ptr)listenerPtr;
    EnumTokenKind  tokenKind = tree_get_tokenKind(node);
    switch (tokenKind) {
    case TK_OP_OR:       case TK_OP_AND:
        {
            t_codeattr_ptr  optrAttr =  codeattr_new();
            optrAttr->firstCodeId = codemgr_nextstat(pEmitter->ircodemgrPtr);
            tree_set_annotation( node, optrAttr );
        }
        return ;

    case TK_KW_ELSE:
        {   // emit a "goto -", make chain, record chain into parent.tc
            t_ircode_ptr gotoCode = codemgr_emit(pEmitter->ircodemgrPtr,
                            OP_GOTO, NULL, NULL,
                            codemgr_unknownJumpAddress(pEmitter->ircodemgrPtr));
            gotoCode->comment.text = CODEDESC_ELSE ", " CODEDESC_BACKPATCHED ;

            // make-chain => ELSE.nc,
            // ELSE.firstCode:  the first code of else-branch
            t_codeattr_ptr  elseAttr =  codeattr_new();
            elseAttr->nextChain = emitter_makeChain(gotoCode, CODEDESC_ELSE CHAIN_NC);
            elseAttr->firstCodeId = codemgr_nextstat(pEmitter->ircodemgrPtr);
            tree_set_annotation( node, elseAttr );
        }
        return;

    default: break;
    }
}
